package metodosRaicesEcuaciones;

import excepciones.RaizNoEncontradaExcepcion;
import funciones.FuncionX;

/**
 * <p>
 * El metodo de Newton-Raphson (tambien conocido como metodo de Newton o metodo
 * de Newton-Fourier) busca aproximaciones a las raices de una funcion f real.
 * </p>
 * <p>
 * Para poder aplicar el metodo se debe verificar que f(x), f'(x) y f''(x) son
 * continuas en el intervalo [a,b] donde se quiere encontrar la raiz.<br>
 * Las condiciones de convergencia del metodo son:<br>
 * 
 * 1) f(a)*f(b)<0 (tienen signos opuestos, por lo tanto por el Teorema de Bolzano
 * 				  hay al menos una raiz en el intervalo)<br>
 * 2) f'(x)!=0 para todo x perteneciente al intervalo [a,b] <br>
 * 3) f''(x)<=0 para todo x perteneciente al intervalo [a,b] <br>
 * 
 * </p>
 * <p>
 * Si no se cumplen las condiciones de convergencia se puede aplicar el metodo pero
 * no se garantiza que converga. La unica manera de alcanzar la convergencia es seleccionar
 * un valor inicial lo suficientemente cercano a la raiz buscada. <br>
 * Asi, se comienza
 * la iteracion con un valor razonablemente cercano al cero. La relativa cercania
 * del punto inicial a la raiz depende mucho de la naturaleza de la propia funcion;
 * si esta presenta multiples puntos de inflexion o pendientes grandes en el entorno
 * de la raiz, entonces las probabilidades de que el algoritmo diverja aumentan, lo
 * cual exige seleccionar un valor supuesto cercano a la raiz.
 * <br> Una vez se ha hecho esto,
 * el metodo linealiza la funcion por la recta tangente en ese valor supuesto. La abscisa
 * en el origen de dicha recta sera segun el metodo, una mejor aproximacion de la raiz
 * que el valor anterior. <br>
 * Se realizaran sucesivas iteraciones hasta que el metodo haya
 * convergido lo suficiente.
 * </p>
 * <p>
 * 
 * Se empieza con un valor inicial X0: <br>
 * Si el signo de f(a) es igual al signo de f''(a), x0=a. <br>
 * Si el signo de f(b) es igual al signo de f''(b), x0=b. <br>
 * 
 * </p>
 * <br>
 * Luego
 * <center>
 * <img alt="NewtonRaphson Formule" src="..\resources\NR.png" > <br>
 * </center>
 * <p>
 * Se itera hasta encontrar la raiz o hasta que el valor encontrado sea menor que el error.
 * </p>
 */
public class NewtonRaphson{
	
	private static final String METHOD_NAME = "NewtonRaphson";
	

    /**
     * El metodo encuentra la raiz de la funcion usando Newton-Raphson
     *
     * @param f funcion     funcion de la cual se quiere obtener la raiz
     * @param fderived      la derivada de la funcion f
     * @param x0            punto de inicio
     * @param error         margen de error
     * @param maxIterations iteraciones permitidas
     * @return raiz de la funcion
     * @throws RaizNoEncontradaExcepcion si no se encuentra la raiz o
     * si la funcion no cumple con las condiciones minimas necesarias
     */
    public static double findRoot(FuncionX f, FuncionX fderived, double x0, double error, int maxIterations)
            throws RaizNoEncontradaExcepcion {
        int iterations = 1;
        double x = x0;
        while (iterations < maxIterations) {
            //Xn+1 = Xn - f(x) / f'(x)
            double x1 = x - f.eval(x) / fderived.eval(x);
            if (Math.abs(f.eval(x1)) < error) {
                return x1;
            }
            x = x1;
            iterations++;
        }
        throw new RaizNoEncontradaExcepcion(METHOD_NAME);
    }
}
